﻿using System;
using System.Linq.Expressions;
using smp.QueryDb.Structure;

namespace smp.QueryDb
{
    public class Filter<TViewModel>
    {
        private readonly FilterStructure _structure;
        
        public Filter(Expression<Func<TViewModel, object>> propertyExpression)
        {
            var property = Helper.GetExpressionProperty(propertyExpression.Body);
            if(property == null)
            {
                throw Helper.Error("Invalid property expression {0}, for filter", propertyExpression.Body);
            }
            _structure = new FilterStructure(property);
        }

        public Filter(Column column)
        {
            _structure = new FilterStructure(column);
        }

        public Filter(string propertyName)
        {
            var property = Helper.GetNamedProperty<TViewModel>(propertyName);
            if (property == null)
            {
                throw Helper.Error("Invalid property name {0} on entity {1}",
                                   propertyName,
                                   typeof (TViewModel).Name);
            }
            _structure = new FilterStructure(property);
        }

        internal FilterStructure Structure {get { return _structure; } }

        public IFilterOperator<TViewModel> Is
        {
            get { return new FilterOperator<TViewModel>(this, false); }
        }

        public IFilterOperator<TViewModel> IsNot
        {
            get
            {
                return new FilterOperator<TViewModel>(this, true);
            }
        }

        public Filter<TViewModel> And(Expression<Func<TViewModel, object>> propertyExpression)
        {
            var newFilter = new Filter<TViewModel>(propertyExpression);
            newFilter.And(this);
            return newFilter;
        }

        public Filter<TViewModel> And(Column column)
        {
            var newFilter = new Filter<TViewModel>(column);
            newFilter.And(this);
            return newFilter;
        }

        public Filter<TViewModel> And(string propertyName)
        {
            var newFilter = new Filter<TViewModel>(propertyName);
            newFilter.And(this);
            return newFilter;
        }

        public Filter<TViewModel> And(Filter<TViewModel> filter)
        {
            if(!_structure.HasBaseOperation)
            {
                throw Helper.Error("Invalid Filter And statement, you must define the base operation before applying logical gates.");
            }
            filter.Structure.SetLogic("AND");
            _structure.AddAdditionalFilter(filter.Structure);
            return this;
        }

        public Filter<TViewModel> Or(Expression<Func<TViewModel, object>> propertyExpression)
        {
            var newFilter = new Filter<TViewModel>(propertyExpression);
            newFilter.Or(this);
            return newFilter;
        }

        public Filter<TViewModel> Or(Column column)
        {
            var newFilter = new Filter<TViewModel>(column);
            newFilter.Or(this);
            return newFilter;
        }

        public Filter<TViewModel> Or(string propertyName)
        {
            var newFilter = new Filter<TViewModel>(propertyName);
            newFilter.And(this);
            return newFilter;
        }

        public Filter<TViewModel> Or(Filter<TViewModel> filter)
        {
            if (!_structure.HasBaseOperation)
            {
                throw Helper.Error("Invalid Filter And statement, you must define the base operation before applying logical gates.");
            }
            filter.Structure.SetLogic("OR");
            _structure.AddAdditionalFilter(filter.Structure);
            return this;
        }
    }
}
